已經有邦友在抱怨沒什麼新鮮事好聊了,以前這種『上網倦怠症』會由女王定期的話題來化解,不過不知道女王是否也受到『上網倦怠症』的荼毒而呈半退休狀態 ...因此野人獻曝貢獻一個可以閒嗑牙的話題。
敝人剛好最近在處理一個問題,把經過分享一下,也許看不下去最近問題太簡單的資深邦友們,會有什麼建議或心得也說不定。
事情是醬子的。最近一個案子是做系統監控的 Web AP,架構是 LAMP,前台使用 EXT-JS 拉畫面及 AJAX。系統需求不是太複雜,完成了。它跟一般瀏覽網站的不一樣之處,在於因為是監控,所以網頁在登入、進到監控頁面之後就不再換頁,持續跑AJAX做監控資料的更新顯示 .... 直到天荒地老。
問題來了。大家都知道瀏覽器一般的使用情境是開啟頁面,看幾秒幾分之後,就會點連結跳走,很少會留在一個固定頁面數天、數週甚至數個月都不切換的。所以瀏覽器的設計理念,在單一頁面中就是持續一直要記憶體來使用,幾乎不 care 釋不釋放記憶體的問題,因為反正換頁後整塊記憶體就會 drop 掉,一了百了,所以幹嘛那麼麻煩去 take care 每一次 allocate 的記憶體到底釋放沒。也因此,如果單純停在某個頁面而持續做 DOM 的改變、持續做 AJAX 的存取,那麼記憶體就會像最近的失業率一樣,如火箭般地衝向月球。
所以原本必須持續數個月監控的 Web 系統,在幾個小時之後,瀏覽器就會死給你看。如果還搭配 Socket.IO 做 comet,則死傷更慘烈。五大瀏覽器,無一倖免,全都會在幾個小時內把 Windows 記憶體塞爆,並停止運作。其中 IE 測試的版本 IE8 最慘,只要 <img> 換圖就升,Temporary Internet Files 檔案過多也掛,IE9 也不遑多讓,有更別出心裁的 mem leak ...
什麼!監控系統只能監控數小時?!這什麼鳥東西?!但我們 RD 又不能跟老闆、客戶說:「啊就因為瀏覽器會 memory leak,所以系統只有三小時限定。要不然我們免費附贈一個三小時後重載網頁的功能好了。」這麼說的話一定會被直接釘在牆壁當標本。事情也不是推給 IE, Firefox,Chrome 就算了。問題是瀏覽器的記憶體,Javascript 再怎麼厲害也沒有權限幫它做 Garbage Collection 啊!但老闆、客戶可不管什麼權不權限的。他們只有期限,沒有權限。限期解決,否則究責。
看倌們!請問您會從什麼方面來解這問題?改用不同的 comet 技術?使用外掛元件做 GC?額外寫支程式躲在背景監控瀏覽器?還是乾脆開發個 Windows AP 來取代 Web?說說您的看法。
少林寺方丈出手,果然有一套.......鼓掌灑花先!!
wiseguy大可要有頭有尾,十天半月後要結論一下,讓眾囉囉謄寫一翻!!
那當然。小弟是有些解決的方案,不過老實說都不是最完美的解。後續會做個總結,分享給大家參考參考。
wiseguy提到:
後續會做個總結
因為我都看不太懂,所以我的方法很簡單,就是叫 工程師 幫我限期解決,管你用什麼方法..
如果我是工程師,則用一個定時裝置 每幾分鐘 按一次F5 重載網頁,
不知道能不能解決.
reload 是沒用的,記憶體繼續膨脹。而且監控系統畫面給它 reload 客戶會以為遭駭了。電影裡的 mission impossible 都是這麼演的。
您這是Memory Leak的問題吧? 是Event的Handler失聯了而無法被釋放嗎? 我看我的監控系統運作時, firefox的GC有在運作啊.
您的更新頻率是? 我可以試試看是否我的監控系統也會有這問題.
很難說是Memory Leak的問題。因為通常說Memory Leak是指《有遺漏未回收的記憶體》,但我上面有說明,因為瀏覽器天生就不是用來擺長時間的,所以也很有可能是《programmer 故意不回收》,只等該 process 終結後就一了百了。所以在這樣的情況下,瀏覽器開發者是不會把它當做 bug 修正的。
您已經提到一個關鍵點:Event 的 Handler 如果是匿名函式,就很可能會 memory leak。我實驗的結果是:如果用完了不設為 null,那也會一直佔用著。如果匿名函式裡又曾建立新物件而沒設為 null 釋放,那 Handler 設為 null 也是無效的。這是 IE8 的結果。
comet 是以 long pulling 在運作,不見得有固定頻率。不過假如一直有資料在異動的話,是 0.5 秒 polling 一次。
當記憶體不斷耗用, 但並非程式所控, 不正是memory leak嗎? 可能是某些handler或物件被誤以為是回收再用, 但實際上是一直建新的, 由於設計誤解, 導致memory leak.
也許是EXT-JS的問題?
google: EXT-JS memory leak
一般人寫 javascript 根本不會理會什麼記憶體釋放問題,甚至可能根本不知道 js 有 gc 這回事。programmer 並非故意不回收,只是在生命週期短暫的網頁上,不管是瀏覽器設計者或 js 設計者,都理所當然的認為記憶體耗用問題不是重點,拼命用就對了,因為沒多久網頁就會被關掉。
所以 EXT-JS、Socket.IO 這類知名的 Javascript Framework 都有嚴重的 memory leak 問題,檢視其程式碼,沒錯,就是拼命 new 物件,從不會去考慮什麼回收問題。開發者沒思考也不打算思考網頁要跑一天、一週、一個月這種 case 吧。
wiseguy提到:
一般人寫 javascript 根本不會理會什麼記憶體釋放問題,甚至可能根本不知道 js 有 gc 這回事。
是這樣喔, 難道都沒學過C? Java?之類的語言? 我之前寫JEE系統, 即使JEE對物件的生命週期設計十分嚴謹, 每一個物件用完之後, 也不管是否會自動被回收, 都會null掉, 呼叫, 迴圈, 或遞迴時, 都反覆檢查物件的生命週期, 生怕一個不注意, 系統就呆死了. 看來, EXT-JS不是穩定的產品?
喔!我想 bizpro 應該是沒有寫過 Javascript 等動態網頁程式。這麼說沒有任何批判貶抑的意思,只是說明如果您有寫過,應該就明白我上面的說法。
通常 Javascript 程式生命期是很短的,就跟 DOS/Windows 裡的 .bat 批次檔一樣,很快地跑過去就整個結束了,短到不會有人關心 bat 檔裡面有沒有指令會造成 memory leak 的問題。但是假如真的有問題,那麼就會在它跑很久很久的時候,問題就會放大、就會影響到穩定性了。
C/Java 都是通用型程式語言,跟網頁用的 Javascript 是截然不同的兩個世界。只能說 Javascript 有它天生的限制,而我們想克服的,就是在這限制下,儘量符合客戶的需求。
EXT-JS 是很知名的 Javascript Framework,但是它也一樣有問題,就證明了我所說的,沒有人會關心 Javascript 跑很久很久的 case。因為瀏覽器天生就不是用來跑很久很久的網頁。所以您說《EXT-JS不是穩定的產品》,就像是說《法拉利沒辦法溯溪》一樣。因為 EXT-JS 不關注穩定,它關注的是快速開發。穩定會在大部份短暫的執行時間內被忽略不計。
講白話一點就是,EXT-JS 的設計者認為還沒到讓它當掉的時間,user 早就已經關掉網頁了,所以譬如《它跑三天就會當掉》這種問題,設計者是一點都不關心的。
是不是寫過Javascript不是重點, 而我已不寫程式了, 程式的語法都差不多, 就像我沒氣力去學PHP了, 但是我還是修正了某一個開源監控系統的某些PHP的關鍵錯誤. 也許Vaadin 7.0的到來會讓我有動機再去寫系統. 不過, 謝謝您的指教.
程式的法則是一樣的: 不因善小而不為. 所謂的善, 是任何可以使系統穩定的程式, 有可能一個小善像是堵住荷蘭堤防的小洞, 卻堵住了未來的大災難. 很難相信不去處理memory leak的根本問題, 任何的workaround都像貼膏藥一樣.
wiseguy提到:
講白話一點就是,EXT-JS 的設計者認為還沒到讓它當掉的時間,user 早就已經關掉網頁了,所以譬如《它跑三天就會當掉》這種問題,設計者是一點都不關心的。
如果EXT-JS是如此的弱的話, 為何您還要用?
因為 EXT-JS 百利而只有這一壞 (mem leak 會導致長時間運作下 crash)。
而且在 99.9% 的 case 中,這一壞是幾乎沒機會發生的。
我會貼出這個問題,就是因為中了這 0.1% 的發生機率,所以才有研究的價值囉~
改成傳統寫法
定時更新網頁
謎之音:畫面不時「閃」一下,程式才像在正常執行
不是股票即時行情
應該沒有人會一直盯著螢幕看吧
我以前寫的監控程式都沒畫面的,出事再哇哇叫就好了...
cdfu提到:
出事再哇哇叫就好了...
我都是等臭蟲鑽出來,程式掛掉後幾天才被告知滴.....
我以前根本沒寫監控程式
都是採用「人工智慧主動式偵測回報系統」
運作原理很簡單
每當系統掛掉
最早發現的人會通知最急的那個人
然後再來向我回報
本來實際上線應該是沒人會一直盯著螢幕看才對。只可惜驗收的時候會 ...
https://developers.google.com/chrome-developer-tools/docs/heap-profiling
Chrome的開發者工具,可以找出已經從dom tree被detach但是還無法釋放的元素,然後參考這些資訊看要怎麼修改程式。
感謝 fillano 大大的資訊,的確用這個找到不少遺漏的地方。不過如果是 EXT-JS 本身的問題,還真困擾改了之後,下一版也許還要來一次 ...
extjs喔...這就傷腦筋XD
我也支持AP+1
超討厭到處都在用瀏覽器!!
除了免安裝軟體(爛藉口,外面一對可攜軟體不是嗎?)
或是所謂跨平台...
奇怪ㄟ,不就畫面顯示而已,其中一台電腦來監控,另外一台抓資料呈現到WEB上
這樣不是很簡單嗎?
不過,我不會寫程式,以上都只是隨口縮縮.....
iT邦幫忙MVPbigcandy提到:
我也支持AP+1
現在老闆喜歡用移動裝置來“監控”公司一切,若用AP,那得要波到:iOS,Android,Windows Phone,Symbian,BlackBerry....族多不及備載~~
泰大說得沒錯。用 AP 就是有這種困擾,要寫好多套。
但 Web 又得看瀏覽器吃飯,真是《有一好、沒兩好》...
wiseguy提到:
但 Web 又得看瀏覽器吃飯,真是《有一好、沒兩好》...
所幸Web還有W3C業界標準,瀏覽器間的差異(如:Firefox vs IE)遠小於手持裝置間的源溝(如:iOS-Objective C/Android-Java)。
而且瀏覽器還有JavaScript天王與全球各地怪咖加持,生猛程度完全不輸AP。
或許糖叔擔心的是瀏覽器方便之餘的資安隱憂?
資安我比較不單心,憑證、https、VPN,應該都可以吧,不過意外總是難免,就掠過
我想,瀏覽器端只抓Web Server來自監控Server的資料,是否可行?
差別在於非即時資料,不要定時刷新、不要太美觀的版型設計、不要flash、不要圖片,要什麼資料,就選擇+確認之後才會抓到Web Server並呈現給瀏覽者。
wiseguy提到:
所以網頁在登入、進到監控頁面之後就不再換頁,持續跑AJAX做監控資料的更新顯示 .... 直到天荒地老。
要什麼資料,就選擇+確認之後才會.....
如果是這樣靜態作法,應不會有記憶體飆高之苦,問題是“表面不換頁,背景跑AJAX”,乍看之下頁面只有幾個數字或圖表在變,實則記憶體一直allocate卻釋放不掉,結果不是瀏覽器慢到爆,不然就是系統直接死給你看....
另一方面,需長時間使用的AP,不論是Windows或手持式裝置,GC(Garbage Collection)可說是programmer基本功,該null的null,該D的D,GC才能充分回收記憶體,AP跑再久都不怕。
瀏覽器不是沒GC,但不知怎地,威力就沒那麼強,難道是天生DNA使然?
bigcandy 大大說的一連串《不要 ...》我都試過了。但遺憾的是《抓資料》這個動作 (polling) 就會讓記憶體升高,只不過越單純的網頁,升高速度越慢而已。
但回到現實面,老闆、客戶不可能會接受不要太美觀的版型設計、不要flash、不要圖片 ... 這種頁面,因為這是要賣的產品,超炫的畫面是影響售價的重要因素。
瀏覽器的吃記憶體問題的確讓人頭大....
我開一個Firefox所造成的系統lag,絕不亞於開一個VM....
我主要使用的瀏覽器是 Firefox,但越改版越囧 ...
雖然記憶體耗用減低了,不過當掉率也提高了 ...
什麼都不幹,光開著都會當掉,這點就比 IE 爛到掉渣 ...
所知
windows 有出empty.exe工具
http://www.microsoft.com/en-us/download/details.aspx?id=17657
XP / 7 / vista/ 2003 可用
主要是清除使用記憶體
見解若力猶未逮之處
請見諒末見怪
Code Man~(Coder)帥哥們........
自己寫一套瀏覽器呢!?
哈哈 ... bigcandy 大大,您別拿我們 RD 窮開心了
我們被允許的解決時間只有一週不到,要自己寫一套瀏覽器的話,可能跳槽到 Mozilla 基金會比較快 ...
而且它的問題跟 AP 是一樣的:必須要部署到各種平台上。所以 bigcandy 大大這個解決方案,小弟只能先謝過,等到要離職前,就會呈報給長官們參考了。
唉,這部份我只能說,不但力有未逮,個人沒興趣碰,也常常覺得寫程式真的好難
敬佩你們能玩弄Code......
bigcandy提到:
敬佩你們能玩弄Code......
其實大家更敬佩糖叔出入A﹣Z女,還能全身而退.....不要扁我,我自K先...
小弟之前有過類似的經驗,
也是用extjs2.x版!
也確實是因為memory leak的問題讓人很頭痛
要寫的功能不是監視器而是KTV
當時的解決方案是利用
JAVA+SWT+XULRUNNER來架構browser
(xurlrunner可以透過設定檔來開啟javascript的jit功能,及一些校調設定)
然後必須得很注意javascript的circular references
頁面更新的會更新的原件再執行一次
document.getElementById('#xxx').innerHTML = ""
(這個動作對extjs的釋放記憶體有一定的效用)
javascript的function的寫法變成
<pre class="c" name="code">
function test() {
var a = "a";
try {
return a;
} catch (e) {
} finally {
a = null;
delete a;
}
}
利用finally來進行一次強制釋放記憶體
做了這些事情去監看記憶體的使用量
就不會一直節節上升
執行狀況大約一周沒問題(是不是能到一年以上就不得而知了)
再補充狀況,當時的機器環境之惡劣
系統為
cpu: 1.5g (哪牌的忘記了,上網查的實際時脈大約800mhz)
ram: 2g
hd: 2g
os: xpe embedded